package com.test.xq.customdemo.customtools;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.test.xq.customdemo.R;

/**
 * Created by xq on 17/5/2.
 */


//PathMeasure
public class CustomView6 extends View {

    private Paint mPaint = new Paint();
    private int mWidth,mHeight;


    private float currentValue = 0;     // 用于纪录当前的位置,取值范围[0,1]映射Path的整个长度

    private float[] pos;                // 当前点的实际位置
    private float[] tan;                // 当前点的tangent值,用于计算图片所需旋转的角度
    private Bitmap mBitmap;             // 箭头图片
    private Matrix mMatrix;             // 矩阵,用于对图片进行一些操作
    public CustomView6(Context context) {
        super(context,null);
    }

    public CustomView6(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
        init(context);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth=w;
        mHeight=h;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        testGetPosTan(canvas);
    }

    private void init(Context context) {
        pos = new float[2];
        tan = new float[2];
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;       // 缩放图片
        mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.arrow, options);
        mMatrix = new Matrix();
    }

    private void initPaint() {
        mPaint.setColor(Color.BLACK);           // 画笔颜色 - 黑色
        mPaint.setStyle(Paint.Style.STROKE);    // 填充模式 - 描边
        mPaint.setStrokeWidth(10);              // 边框宽度 - 10
    }

    //1、构造函数
    //不论 forceClosed 设置为何种状态(true 或者 false)， 都不会影响原有Path的状态，即 Path 与 PathMeasure 关联之后，之前的的 Path 不会有任何改变。
    //forceClosed 的设置状态可能会影响测量结果，如果 Path 未闭合但在与 PathMeasure 关联的时候设置 forceClosed 为 true 时，测量结果可能会比 Path 实际长度稍长一点，获取到到是该 Path 闭合时的状态。
    private void testForceClosed(Canvas canvas){
        canvas.translate(mWidth/2,mHeight/2);

        Path path = new Path();

        path.lineTo(0,200);
        path.lineTo(200,200);
        path.lineTo(200,0);

        PathMeasure measure1 = new PathMeasure(path,false);
        PathMeasure measure2 = new PathMeasure(path,true);

        Log.e("TAG", "forceClosed=false---->"+measure1.getLength());
        Log.e("TAG", "forceClosed=true----->"+measure2.getLength());

        canvas.drawPath(path,mPaint);
    }

    //2.setPath、 isClosed 和 getLength
    //这三个方法都如字面意思一样，非常简单，这里就简单是叙述一下，不再过多讲解。
    //setPath 是 PathMeasure 与 Path 关联的重要方法，效果和 构造函数 中两个参数的作用是一样的。
    // isClosed 用于判断 Path 是否闭合，但是如果你在关联 Path 的时候设置 forceClosed 为 true 的话，这个方法的返回值则一定为true。
    //getLength 用于获取 Path 的总长度，在之前的测试中已经用过了。


    //3.getSegment
    //getSegment 用于获取Path的一个片段
    //boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)
    //返回值(boolean)	判断截取是否成功	true 表示截取成功，结果存入dst中，false 截取失败，不会改变dst中内容
    //startD	开始截取位置距离 Path 起点的长度	取值范围: 0 <= startD < stopD <= Path总长度
    //stopD	结束截取位置距离 Path 起点的长度	取值范围: 0 <= startD < stopD <= Path总长度
    //dst	截取的 Path 将会添加到 dst 中	注意: 是添加，而不是替换
    //startWithMoveTo	起始点是否使用 moveTo	用于保证截取的 Path 第一个点位置不变

    //我们创建了一个 Path， 并在其中添加了一个矩形，现在我们想截取矩形中的一部分，就是下图中红色的部分。
    //矩形边长400dp，起始点在左上角，顺时针
    private void testGetSegment(Canvas canvas){
        canvas.translate(mWidth/2,mHeight/2);

        Path path = new Path();
        path.addRect(-200,-200,200,200, Path.Direction.CW);

        Path dst = new Path();   // 创建用于存储截取后内容的 Path

        PathMeasure measure = new PathMeasure(path,false);

        // 截取一部分存入dst中，并使用 moveTo 保持截取得到的 Path 第一个点的位置不变
        measure.getSegment(200,600,dst,true);

        canvas.drawPath(dst,mPaint);

    }

    //从上图可以看到我们成功到将需要到片段截取了出来，然而当 dst 中有内容时会怎样呢？
    //可以得到结论：被截取的 Path 片段会添加到 dst 中，而不是替换 dst 中到内容。
    //从该示例我们又可以得到一条结论：如果 startWithMoveTo 为 true, 则被截取出来到Path片段保持原状，如果 startWithMoveTo 为 false，则会将截取出来的 Path 片段的起始点移动到 dst 的最后一个点，以保证 dst 的连续性。
    private void testGetSegment1(Canvas canvas){
        canvas.translate(mWidth / 2, mHeight / 2);          // 平移坐标系

        Path path = new Path();                                     // 创建Path并添加了一个矩形
        path.addRect(-200, -200, 200, 200, Path.Direction.CW);

        Path dst = new Path();                                      // 创建用于存储截取后内容的 Path
        dst.lineTo(-300, -300);                                     // 在 dst 中添加一条线段

        PathMeasure measure = new PathMeasure(path, false);         // 将 Path 与 PathMeasure 关联

        measure.getSegment(200, 600, dst, true);                   // 截取一部分 并使用 moveTo 保持截取得到的 Path 第一个点的位置不变
        //measure.getSegment(200, 600, dst, false);                   // <--- 截取一部分 不使用 startMoveTo, 保持 dst 的连续性

        canvas.drawPath(dst, mPaint);                        // 绘制 Path

    }

    //4.nextContour
    //我们知道 Path 可以由多条曲线构成，但不论是 getLength , getgetSegment 或者是其它方法，都只会在其中第一条线段上运行，而这个 nextContour 就是用于跳转到下一条曲线到方法，如果跳转成功，则返回 true， 如果跳转失败，则返回 false。

    //如下，我们创建了一个 Path 并使其中包含了两个闭合的曲线，内部的边长是200，外面的边长是400，现在我们使用 PathMeasure 分别测量两条曲线的总长度。

    //通过测试，我们可以得到以下内容：

//    1.曲线的顺序与 Path 中添加的顺序有关。
//            2.getLength 获取到到是当前一条曲线分长度，而不是整个 Path 的长度。
//            3.getLength 等方法是针对当前的曲线(其它方法请自行验证)。
    private void testNextContour(Canvas canvas){
        canvas.translate(mWidth / 2, mHeight / 2);      // 平移坐标系

        Path path = new Path();

        path.addRect(-100, -100, 100, 100, Path.Direction.CW);  // 添加小矩形
        path.addRect(-200, -200, 200, 200, Path.Direction.CW);  // 添加大矩形

        canvas.drawPath(path,mPaint);                    // 绘制 Path

        PathMeasure measure = new PathMeasure(path,false);

        float len1 = measure.getLength();  // 获得第一条路径的长度

        measure.nextContour();  // 跳转到下一条路径

        float len2 = measure.getLength();  // 获得第二条路径的长度

        Log.i("LEN","len1="+len1);                              // 输出两条路径的长度
        Log.i("LEN","len2="+len2);
    }

//    5.getPosTan
//    这个方法是用于得到路径上某一长度的位置以及该位置的正切值：
//
//    boolean getPosTan (float distance, float[] pos, float[] tan)
//    方法各个参数释义：
//
//    参数	作用	备注
//    返回值(boolean)	判断获取是否成功	true表示成功，数据会存入 pos 和 tan 中，
//            false 表示失败，pos 和 tan 不会改变
//    distance	距离 Path 起点的长度	取值范围: 0 <= distance <= getLength
//    pos	该点的坐标值	当前点在画布上的位置，有两个数值，分别为x，y坐标。
//    tan	该点的正切值	当前点在曲线上的方向，使用 Math.atan2(tan[1], tan[0]) 获取到正切角的弧度值。

    private void testGetPosTan(Canvas canvas){
        canvas.translate(mWidth / 2, mHeight / 2);      // 平移坐标系

        Path path = new Path();                                 // 创建 Path

        path.addCircle(0, 0, 200, Path.Direction.CW);           // 添加一个圆形

        PathMeasure measure = new PathMeasure(path, false);     // 创建 PathMeasure

        currentValue += 0.005;                                  // 计算当前的位置在总长度上的比例[0,1]
        if (currentValue >= 1) {
            currentValue = 0;
        }

        measure.getPosTan(measure.getLength() * currentValue, pos, tan);        // 获取当前位置的坐标以及趋势

        mMatrix.reset();                                                        // 重置Matrix
        float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); // 计算图片旋转角度

        mMatrix.postRotate(degrees, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);   // 旋转图片
        mMatrix.postTranslate(pos[0] - mBitmap.getWidth() / 2, pos[1] - mBitmap.getHeight() / 2);   // 将图片绘制中心调整到与当前点重合

        canvas.drawPath(path, mPaint);                                   // 绘制 Path
        canvas.drawBitmap(mBitmap, mMatrix, mPaint);                     // 绘制箭头

        invalidate();
    }
}
